package com.edu.util.file;

import com.edu.model.constant.FileConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class FileUtil implements FileInfo {

    private static final Logger LOGGER = LoggerFactory.getLogger(FileUtil.class);

    private File file;

    private List<File> files;

    private File zipFile;

    private String prefix;

    private String filepath;

    @Override
    public String getFilename() {
        return null;
    }

    @Override
    public String getFilepath() {
        return this.filepath;
    }

    @Override
    public String getZipFilename() {
        return this.zipFile.getName();
    }

    @Override
    public String getZipPath() throws IOException {
        return this.zipFile.getCanonicalPath();
    }

    /**
     * 创建文件
     *
     * @param filepath  文件路径，默认为
     * @param prefix    文件名前缀，默认为tem_
     * @param extension 文件扩展名，默认为.txt
     * @param filename  文件名称，必填
     * @throws IOException
     * @see FileStorage
     */
    public void createFile(String filepath, String prefix, String extension, String filename) throws IOException {
        if (filename == null) {
            throw new IllegalArgumentException("filename is null");
        }
        this.prefix = prefix == null ? FileConfig.DEFAULT_PREFIX : prefix;
        extension = extension == null ? FileConfig.DEFAULT_SUFFIX : extension;
        this.filepath = filepath == null ? FileConfig.DEFAULT_CREATE_PATH : filepath;
        File mkFile = new File(filepath);
        if (!mkFile.exists()) {
            mkFile.mkdirs();
        }
        this.file = new File(filepath, filename + extension);
        this.file.createNewFile();
    }

    /**
     * 创建临时文件
     *
     * @param prefix    文件名前缀，默认为tem_
     * @param extension 文件扩展名，默认为.txt
     * @param filepath  文件路径，非必填
     * @throws IOException
     */
    public void createTempFile(String prefix, String extension, String filepath) throws IOException {
        this.prefix = prefix == null ? FileConfig.DEFAULT_PREFIX : prefix;
        extension = extension == null ? FileConfig.DEFAULT_SUFFIX : extension;
        this.filepath = filepath;
        if (filepath == null) {
            this.file = File.createTempFile(this.prefix, extension);
        } else {
            this.file = File.createTempFile(this.prefix, extension, new File(filepath));
        }
    }

    public void appendModuleFile(Map<String, String> map, String extension) throws IOException {
        if (map == null || map.isEmpty()) {
            return;
        }
        extension = extension == null ? FileConfig.JS_SUFFIX : extension;
        files = new ArrayList<>(map.size());
        Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
        File mFile = null;
        while (iterator.hasNext()) {
            Map.Entry<String, String> next = iterator.next();
            mFile = new File(this.filepath, next.getKey() + extension);
            mFile.createNewFile();
            try (FileWriter fileWriter = new FileWriter(mFile)) {
                fileWriter.write(next.getValue());
                fileWriter.flush();
            } catch (IOException e) {
                throw e;
            }
            files.add(mFile);
        }
    }

    /**
     * 向文件中添加内容
     *
     * @param content
     */
    public void append(String content) throws IOException {
        append(content, null);
    }

    public void append(String mainContent, List<String> contents) throws IOException {
        append(null, mainContent, contents, null);
    }

    public void append(String head, String mainContent, List<String> contents, String tail) throws IOException {
        if (file == null) {
            return;
        }
        if (mainContent == null && contents == null) {
            return;
        }
        try (FileWriter fileWriter = new FileWriter(file)) {

            if (head != null) {
                fileWriter.write(head);
            }
            if (mainContent != null) {
                fileWriter.write(mainContent);
            }
            if (contents != null && !contents.isEmpty()) {
                for (String content : contents) {
                    fileWriter.write(content);
                }
            }
            if (tail != null) {
                fileWriter.write(tail);
            }
            fileWriter.flush();
        } catch (IOException e) {
            throw e;
        }
    }

    /**
     * 将文件转为zip压缩包
     *
     * @throws IOException
     */
    public void toZip() throws IOException {
        if (file == null) {
            return;
        }
        if (filepath == null) {
            this.zipFile = File.createTempFile(this.prefix, FileConfig.ZIP_SUFFIX);
        } else {
            this.zipFile = File.createTempFile(this.prefix, FileConfig.ZIP_SUFFIX, new File(filepath));
        }
        try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile));
             WritableByteChannel writableByteChannel = Channels.newChannel(zipOut);
             FileChannel fileChannel = new FileInputStream(file).getChannel()) {
            zipOut.putNextEntry(new ZipEntry(file.getName()));
            fileChannel.transferTo(0, file.length(), writableByteChannel);

            if (files != null && !files.isEmpty()) {
                for (File moduleFile : files) {
                    FileChannel mFile = new FileInputStream(moduleFile).getChannel();
                    zipOut.putNextEntry(new ZipEntry(moduleFile.getName()));
                    mFile.transferTo(0, moduleFile.length(), writableByteChannel);
                    mFile.close();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void deleteFile() {
        if (file != null) {
            file.delete();
        }
        if (zipFile != null) {
            zipFile.delete();
        }
        if (files != null) {
            for (File mFile : files) {
                mFile.delete();
            }
        }
    }

    public InputStream getZipStream() throws FileNotFoundException {
        if (zipFile == null) {
            return null;
        }
        return new FileInputStream(zipFile);
    }

    public List<File> getFiles() {
        return files;
    }

    /**
     * 存储本文件生成的zip
     *
     * @param fileStorage
     * @param <T>
     * @return
     * @throws Exception
     */
    public <T> T store(FileStorage<T> fileStorage) throws Exception {
        InputStream zipStream = this.getZipStream();
        if (zipStream == null) {
            return null;
        }
        return exeStore(fileStorage, zipStream, this);
    }

    /**
     * 存储自定义内容
     *
     * @param fileStorage
     * @param stream
     * @param <T>
     * @return
     * @throws Exception
     */
    public <T> T store(FileStorage<T> fileStorage, InputStream stream) throws Exception {
        if (stream == null) {
            return null;
        }
        return exeStore(fileStorage, stream, this);
    }

    private <T> T exeStore(FileStorage<T> fileStorage, InputStream stream, FileInfo fileInfo) throws Exception {

        T obj;
        try {
            obj = fileStorage.doStore(stream, fileInfo);
        } finally {
            try {
                if (stream != null) {
                    stream.close();
                }
            } catch (Exception e) {
                LOGGER.error("file storage's input stream close error {}", e.getMessage());
            } finally {
                if (fileStorage.isDeleteAfterStore()) {
                    this.deleteFile();
                }
            }
        }
        return obj;
    }
}
